package com.plugin.manage.common;

import com.plugin.core.bean.LogAop;
import com.plugin.core.model.entity.SysLog;
import com.plugin.manage.service.ISysLogService;

import jakarta.servlet.ServletInputStream;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * 方法级日志切面组件
 **/

@Component
@Aspect
@Slf4j
public class MethodLogAop {


    @Autowired
    private ISysLogService sysLogService;




    /**
     * 异步处理线程池
     */
    private final static ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);

    /**
     * 切点
     */
    @Pointcut("@annotation(com.plugin.core.bean.LogAop)")
    public void methodPointcut() { }


    /**
     * 切面
     */
    @Around("methodPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        SysLog sysLog = new SysLog();

        jakarta.servlet.http.HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        StringBuilder sb = new StringBuilder();
        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("读取请求体失败", e);
        }

        sysLog.setReqParam(sb.toString());
        sysLog.setMethodRemark(getAnnotationRemark(point));

        String methodName = point.getSignature().getName();
        String packageName = point.getThis().getClass().getName();
        if (packageName.contains("$$EnhancerByCGLIB$$") || packageName.contains("$$EnhancerBySpringCGLIB$$")) {
            packageName = packageName.substring(0, packageName.indexOf("$$"));
        }
        sysLog.setMethod(packageName + "." + methodName);

        Object result;
        try {
            result = point.proceed();
        } catch (Throwable t) {
            log.error("执行切面方法时发生异常", t);
            throw t;
        }

        try {
            sysLog.setOperName(request.getParameter("username") == null ? "" : request.getParameter("username"));
            sysLog.setSysType(1);
            sysLog.setOperIp(request.getRemoteHost());
            sysLog.setOperTime(LocalDateTime.now());
            sysLog.setOperUrl(request.getRequestURI());

            if (sysLogService != null) {
                scheduledThreadPool.execute(() -> sysLogService.add(sysLog));
            }
        } catch (Exception e) {
            log.error("methodLogError", e);
        }

        return result;
    }


    /**
     * 获取方法中的中文备注
     */
    public static String getAnnotationRemark(ProceedingJoinPoint joinPoint) throws Exception {

        Signature sig = joinPoint.getSignature();
        Method m = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(),  ((MethodSignature) sig).getParameterTypes());

        LogAop methodCache = m.getAnnotation(LogAop.class);
        if (methodCache != null) {
            return methodCache.remark();
        }
        return "";
    }


    @Before("methodPointcut()")
    public void logBefore(JoinPoint joinPoint) {
        log.info(joinPoint.getTarget().getClass().getName()+" Method: " + joinPoint.getSignature().getName() + "-------->started.");
        log.info("Arguments: " + Arrays.toString(joinPoint.getArgs()));
    }

    @After("methodPointcut()")
    public void logAfter(JoinPoint joinPoint) {
        log.info(joinPoint.getTarget().getClass().getName()+" Method: " + joinPoint.getSignature().getName() + "<--------finished.");
    }
}
